#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
// #include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define TIMER_CNT 	1 				/* 设备号个数 */
#define TIMER_NAME 	"myTimer" 		/* 名字 */
#define CLOSE_CMD 	(_IO(0XEF,	0X1)) 	/* 关闭定时器 */
#define OPEN_CMD  	(_IO(0XEF,	0X2)) 	/* 打开定时器 */
#define SETTIME_CMD (_IO(0XEF,	0X3)) 	/* 设置定时周期 */
#define LED_ON 1 					/* 开灯 */
#define LED_OFF 0 					/* 关灯 */
#define LED_NAME "led-gpio" 					/* 关灯 */

/* 设备结构体 */
struct mytimer_dev{
	dev_t devid; 			/* 设备号 */
	struct cdev cdev; 		/* cdev */
	struct class *class;	/* 类 */
	struct device *device; 	/* 设备 */
	int major; 				/* 主设备号 */
	int minor; 				/* 次设备号 */
	struct device_node *nd; /* 设备节点 */
	int led_gpio;			/* led所使用的GPIO编号 */
	struct timer_list timer;/* 定时器 */
	int timerperiod; 		/* 定时时间 */
	// # unsigned char timer_cnt; /* 定时器使用次数 */
    spinlock_t lock; 		/* 自旋锁 */
};

struct mytimer_dev mytimer; /* 定义一个led设备 */

int led_init(void){
	int ret = 0;
	const char *str;
/* 1、获取设备节点：timerApp */
	mytimer.nd = of_find_node_by_path("/gpioled");
	if(mytimer.nd == NULL){
		printk("timerApp node not find!\r\n");
		return -EINVAL;
	}
	/* 2、获取设备status状态 */
	of_property_read_string(mytimer.nd,"status",&str);
	if (strcmp("okay",str) != 0){
		printk("status is not okay!\r\n");
		return -EINVAL;
	}
	/* 3、获取compatible属性 */
	ret = of_property_read_string(mytimer.nd,"compatible",&str);
	if(ret < 0){
		printk("can't get compatible!\r\n");
		return -EINVAL;
	}
	if(strcmp(str,"wlz,led")){
		printk("compatible is not wlz,led!\r\n");
		return -EINVAL;
	}
	/* 打印设备信息 */
	printk("compatible = %s\r\n",str);
	/* 4、获取led所使用的GPIO编号 */
	mytimer.led_gpio = of_get_named_gpio(mytimer.nd,"led-gpios",0);
	if(mytimer.led_gpio < 0){
		printk("can't get led-gpios\r\n");
		return -EINVAL;
	}
	/* 5、初始化LED所使用的GPIO */
	ret = gpio_request(mytimer.led_gpio,"led-gpios");
	if( ret < 0){
		printk("Fialed to request led-gpios!\r\n");
		return -EINVAL;
	}
	/* 6、设置为输出，默认高电平 */
	ret = gpio_direction_output(mytimer.led_gpio,1);
	if(ret < 0){
		printk("can't set led-gpios!\r\n");
		return -EINVAL;
	}
	return 0;
}
/* 打开设备 */
static int timerApp_open(struct inode *inode, struct file *filp){
	int ret ;
	filp->private_data = &mytimer; /* 设置私有数据 */
	mytimer.timerperiod = 1000;
	ret = led_init();
	if(ret < 0){
		printk("timerApp init failed!\r\n");
		return -EINVAL;
	}
	return 0;
}

static	long timerApp_ioctl (struct file * filp, unsigned int cmd, unsigned long arg){

	int timerperiod;
	unsigned long flags;
	struct mytimer_dev *dev = (struct mytimer_dev *)filp->private_data;
	switch(cmd){
		case CLOSE_CMD: /* 关闭定时器 */
			del_timer_sync(&dev->timer);
			break;
		case OPEN_CMD: /* 打开定时器 */
			spin_lock_irqsave(&dev->lock,flags);
			timerperiod = dev->timerperiod;
			spin_unlock_irqrestore(&dev->lock,flags);
			/* 启动定时器 */
			mod_timer(&dev->timer,jiffies + msecs_to_jiffies(timerperiod));
			break;
		case SETTIME_CMD: /* 设置定时时间 */
			spin_lock_irqsave(&dev->lock,flags);
			dev->timerperiod = arg;
			spin_unlock_irqrestore(&dev->lock,flags);
			mod_timer(&dev->timer,jiffies + msecs_to_jiffies(timerperiod));
			break;
		default:
			break;
	}
	return 0;
	
}
/* 释放设备 */
static int timerApp_release(struct inode *inode, struct file *filp)
{
	struct mytimer_dev *dev = (struct mytimer_dev *)filp->private_data;
	/* 关闭LED */
	gpio_set_value(dev->led_gpio,LED_OFF); 		/* 关闭LED */
	gpio_free(dev->led_gpio); 					/* 释放LED */
	del_timer_sync(&dev->timer);				/* 关闭定时器 */
	return 0;
}

/* 定时器回调函数 */
void timer_function(struct timer_list *arg){

	struct mytimer_dev *dev = from_timer(dev,arg,timer);
	static int sta = 1 ;
	int timerperiod ;
	unsigned long flags;
	/* 改变LED状态 */
	sta =!sta;
	/* 改变LED状态 */
	gpio_set_value(dev->led_gpio,sta);
	/* 获取定时器周期 */
	spin_lock_irqsave(&dev->lock,flags);
	timerperiod = dev->timerperiod;
	spin_unlock_irqrestore(&dev->lock,flags);
 
	/* 重新启动定时器 */
	mod_timer(&dev->timer,jiffies + msecs_to_jiffies(timerperiod));
}

struct file_operations timerApp_fops = {
	.owner = THIS_MODULE,
	.open = timerApp_open,
	.release = timerApp_release,
	.unlocked_ioctl = timerApp_ioctl, /* 注册ioctl */
	.probe = timerApp_open,
};


/* 定时器初始化 */
int timerApp_init(void){
    int ret;
	/* 1、初始化自旋锁 */
	spin_lock_init(&mytimer.lock);
	/* 2、注册字符设备驱动 */
	/* 2.1 创建设备号 */
	if(mytimer.major){ /* 定义了设备号 */
		mytimer.devid = MKDEV(mytimer.major,0);
		ret = register_chrdev_region(mytimer.devid,TIMER_CNT,LED_NAME); /* 静态注册设备号 */
        if (ret < 0){
            pr_err("register_chrdev_region failed!\r\n");
            return -EIO;
        }
        
	}else{ /* 没有定义设备号 */
		ret = alloc_chrdev_region(&mytimer.devid,0,TIMER_CNT,LED_NAME); /* 动态申请设备号 */
		mytimer.major = MAJOR(mytimer.devid); /* 获取主设备号 */
		mytimer.minor = MINOR(mytimer.devid); /* 获取次设备号 */
	}
	printk("timerApp major=%d,minor=%d\r\n",mytimer.major,mytimer.minor);
	/* 2.2 初始化cdev */
	mytimer.cdev.owner = THIS_MODULE;
	cdev_init(&mytimer.cdev,&timerApp_fops);
	/* 2.3 添加一个cdev */
	ret = cdev_add(&mytimer.cdev,mytimer.devid,TIMER_CNT);
	if(ret < 0){
		pr_err("cdev_add failed!\r\n");
		goto del_unregister;
	}
	/* 2.4 创建类 */
	mytimer.class = class_create(THIS_MODULE,LED_NAME);
	if(IS_ERR(mytimer.class)){
		goto del_cdev;
	}
	/* 2.5 创建设备 */
	mytimer.device = device_create(mytimer.class, NULL, mytimer.devid, NULL, LED_NAME);
	if (IS_ERR(mytimer.device)){
		goto destory_class;
	}
	/* 3、初始化定时器 */
	/* 3.1 初始化定时器 */
	timer_setup(&mytimer.timer, timer_function, 0);
	 
	return 0;

destory_class:
	class_destroy(mytimer.class);
del_cdev:
	cdev_del(&mytimer.cdev);
del_unregister:
	unregister_chrdev_region(mytimer.devid,TIMER_CNT);
	return -EIO;
}
/* 定时器退出 */
void timerApp_exit(void)
{

	/* 1、删除定时器 */
	del_timer(&mytimer.timer);
	/* 2、释放使用的GPIO */
	gpio_free(mytimer.led_gpio);
	/* 3、释放设备节点 */
	of_node_put(mytimer.nd);

    cdev_del(&mytimer.cdev); /* 删除cdev */
	unregister_chrdev_region(mytimer.devid, TIMER_CNT); /* 注销设备号 */
    device_destroy(mytimer.class, mytimer.devid); /* 注销设备 */
    class_destroy(mytimer.class); /* 注销类 */
}  




module_init(timerApp_init);
module_exit(timerApp_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wang");
MODULE_INFO(intree, "Y");